﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Runtime.InteropServices;
using MiscUtil;

namespace Kise.LprCapturer
{
    class TcpReceiver : IFrameCapture
    {
        private IPEndPoint _endpoint;
        private Thread _receiveThread;
        private Thread _workerThread;
        private volatile bool _run = true;
        private TcpClient _tcpSocket;
        private ConcurrentQueue<Frame> _frameQueue = new ConcurrentQueue<Frame>();
        private const int MaxLen = 10 * 1024 * 1024;

        public event EventHandler<MiscUtil.EventArgs<Frame>> NewFrameCaptured;

        public void RaiseNewFrameReceived(EventArgs<Frame> e)
        {
            EventHandler<EventArgs<Frame>> handler = NewFrameCaptured;
            if (handler != null) handler(this, e);
        }

        public TcpReceiver(System.Net.IPEndPoint endpoint)
        {
            _endpoint = endpoint;

        }

        public void Start()
        {
            if (_receiveThread == null)
            {
                _receiveThread = new Thread(ReceiveThread);
                _receiveThread.IsBackground = true;
                _receiveThread.Start();

                _workerThread = new Thread(WorkerThread);
                _workerThread.IsBackground = true;
                _workerThread.Start();
            }
        }

        public void Stop()
        {
            this._run = false;
        }

        private void WorkerThread(object state)
        {
            while (_run)
            {
                Frame frame;
                if (_frameQueue.TryDequeue(out frame))
                {
                    try
                    {
                        //var img = ConvertToBitmap(frame);
                        //frame.JpegBuffer = img.ToArray();
                        RaiseNewFrameReceived(new EventArgs<Frame>(frame));
                    }
                    catch (Exception ex)
                    {
                        System.Diagnostics.Debug.WriteLine(ex);
                    }
                }
                else
                {
                    Thread.Sleep(50);
                }
            }

            System.Diagnostics.Debug.WriteLine("end of worker thread");
        }

        private unsafe void ReceiveThread(object state)
        {
            while (_run)
            {
                try
                {
                    _tcpSocket = new TcpClient();
                    _tcpSocket.Connect(_endpoint);

                    var headerLen = Marshal.SizeOf(typeof(Header));
                    while (_run)
                    {
                        var headerBuffer = new byte[headerLen];
                        var bytesRead = Helper.ReadLength(headerBuffer, 0, headerBuffer.Length, _tcpSocket.GetStream());
                        if (bytesRead != headerBuffer.Length)
                        {
                            CloseSocket();
                            break;
                        }
                        var header = BytesToHeader(headerLen, headerBuffer);

                        var time = new DateTime(
                                header.year,
                                header.month,
                                header.day,
                                header.hour,
                                header.minute,
                                header.second,
                                (int)header.msecond / 1000);

                        var jpegCount = header.img_num;
                        var capturedImages = new List<CapturedImage>(jpegCount);
                        if (jpegCount > 0)
                        {
                            var jpegLen = header.jpeg_len;
                            System.Diagnostics.Debug.WriteLine("len={0}, jpegLen={1}, h264len={2}", header.len, header.jpeg_len, header.h264_len);
                            if (jpegLen > MaxLen)
                            {
                                CloseSocket();
                                break;
                            }
                            var img = new byte[jpegLen];
                            var jpegRead = Helper.ReadLength(img, 0, img.Length, _tcpSocket.GetStream());
                            if (jpegRead != img.Length)
                            {
                                CloseSocket();
                                break;
                            }


                            var capturedImage = new CapturedImage(time, img);
                            capturedImages.Add(capturedImage);
                        }

                        if (jpegCount > 1)
                        {
                            var jpegLen = header.jpeg_len2;
                            System.Diagnostics.Debug.WriteLine("len={0}, jpegLen={1}, h264len={2}", header.len, header.jpeg_len, header.h264_len);
                            if (jpegLen > MaxLen)
                            {
                                CloseSocket();
                                break;
                            }
                            var img = new byte[jpegLen];
                            var jpegRead = Helper.ReadLength(img, 0, img.Length, _tcpSocket.GetStream());
                            if (jpegRead != img.Length)
                            {
                                CloseSocket();
                                break;
                            }
                            var time2 = new DateTime(
                                header.year2,
                                header.month2,
                                header.day2,
                                header.hour2,
                                header.minute2,
                                header.second2,
                                (int)header.msecond2 / 1000);

                            var capturedImage = new CapturedImage(time2, img);
                            capturedImages.Add(capturedImage);
                        }

                        var h264Len = header.h264_len;
                        if (h264Len > MaxLen)
                        {
                            CloseSocket();
                            break;
                        }
                        var h264 = new byte[h264Len];
                        var h264Read = Helper.ReadLength(h264, 0, h264.Length, _tcpSocket.GetStream());
                        if (h264Read != h264Len)
                        {
                            CloseSocket();
                            break;
                        }

                        var arg = new Frame();
                        arg.Images = capturedImages.ToArray();
                        arg.CaptureTime = time;
                        arg.H264 = h264;
                        arg.PlateNumbers = new string[header.plate_num];
                        arg.Speed = header.vehicle_speed;
                        arg.LocationId = header.device_no.ToString();

                        _frameQueue.Enqueue(arg);
                    }
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debug.Write(ex.Message);
                    CloseSocket();
                }
            }

            System.Diagnostics.Debug.WriteLine("end of receiving");
        }

        private unsafe MemoryStream ConvertToBitmap(Frame frame)
        {
            var len = (1632 * 1232) * 3;


            var yuvPtr = Marshal.AllocHGlobal(len);
            // Marshal.Copy(frame.JpegBuffer, 0, yuvPtr, frame.JpegBuffer.Length);
            var rgbPtr = Marshal.AllocHGlobal(len);
            var res = Helper.YUV422_TO_RGB((byte*)yuvPtr.ToPointer(), (byte*)rgbPtr.ToPointer(), 1632, 1232);
            System.Diagnostics.Debug.WriteLine(res);
            Marshal.FreeHGlobal(yuvPtr);

            // ShiftPixel(rgbPtr);

            var bmp = new Bitmap(1632, 1232, 1632 * 3, PixelFormat.Format24bppRgb, rgbPtr);
            var sb = new StringBuilder();
            foreach (var plateNumber in frame.PlateNumbers)
            {
                sb.AppendFormat("{0}-", plateNumber);
            }

            sb.Append(Guid.NewGuid() + ".jpg");

            //bmp.Save(sb.ToString(), ImageFormat.Jpeg);
            var stream = new MemoryStream();
            bmp.Save(stream, System.Drawing.Imaging.ImageFormat.Jpeg);
            bmp.Dispose();
            Marshal.FreeHGlobal(rgbPtr);



            return stream;
        }

        private static unsafe void ShiftPixel(IntPtr rgbPtr)
        {
            byte* pB = (byte*)rgbPtr.ToPointer();
            var tempBuffer = new byte[120 * 3];
            int ii = 0;
            for (Int32 iHeight = 0; iHeight < 1232; iHeight++)
            {
                byte* p = pB + iHeight * 1632 * 3;

                for (ii = 0; ii < 110 * 3; ii++)
                    tempBuffer[ii] = p[(1632 - 110) * 3 + ii];
                for (ii = 0; ii < (1632 - 110) * 3; ii++)
                {
                    p[1632 * 3 - ii] = p[(1632 - 110) * 3 - ii];
                }
                for (ii = 0; ii < 110 * 3; ii++)
                    p[ii] = tempBuffer[ii];
            }
        }

        private void CloseSocket()
        {
            if (_tcpSocket != null && _tcpSocket.Connected)
            {
                _tcpSocket.Close();
                _tcpSocket = null;
            }
        }

        private static Header BytesToHeader(int headerLen, byte[] headerBuffer)
        {
            IntPtr ptr = Marshal.AllocHGlobal(headerLen);
            Marshal.Copy(headerBuffer, 0, ptr, headerLen);
            var header = (Header)Marshal.PtrToStructure(ptr, typeof(Header));
            Marshal.FreeHGlobal(ptr);
            return header;
        }


    }
}
